﻿using System;

namespace Aeon.Emulator.Sound.FM
{
    /// <summary>
    /// Calculates OPL3 tremolo multipliers.
    /// </summary>
    internal sealed class TremoloGenerator
    {
        private const double TremoloFrequency = 3.7;
        private const double InitialValue1 = 1.0;
        private const double InitialValue2 = 4.8;

        private readonly double factor1;
        private readonly double factor2;
        private readonly double tremoloLength;
        private int dam;
        private int currentIndex;

        /// <summary>
        /// Initializes a new instance of the <see cref="TremoloGenerator"/> class.
        /// </summary>
        /// <param name="sampleRate">The sample rate.</param>
        public TremoloGenerator(int sampleRate)
        {
            this.tremoloLength = (int)(sampleRate / TremoloFrequency);

            this.factor1 = 1.0 / sampleRate * (2 * TremoloFrequency);
            this.factor2 = 4.8 / sampleRate * (2 * TremoloFrequency);
        }

        /// <summary>
        /// Gets or sets the DAM value.
        /// </summary>
        public int DAM
        {
            get { return this.dam; }
            set
            {
                if(value < 0 || value > 1)
                    throw new ArgumentOutOfRangeException();

                this.dam = value;
                this.Recalculate();
            }
        }
        /// <summary>
        /// Gets the current tremolo multiplier.
        /// </summary>
        public double CurrentValue { get; private set; }

        /// <summary>
        /// Increments the tremolo index.
        /// </summary>
        public void Increment()
        {
            this.currentIndex++;
            if(this.currentIndex > this.tremoloLength)
                this.currentIndex = 0;

            this.Recalculate();
        }

        /// <summary>
        /// Recalculates the current value.
        /// </summary>
        private void Recalculate()
        {
            if(this.dam == 0)
                this.CurrentValue = -Math.Abs(InitialValue1 - (this.currentIndex * this.factor1));
            else
                this.CurrentValue = -Math.Abs(InitialValue2 - (this.currentIndex * this.factor2));
        }
    }
}
